package xyz.luan.audioplayers

import android.os.Handler
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import java.util.*
import java.util.logging.Level
import java.util.logging.Logger

class AudioplayersPlugin : FlutterPlugin, ActivityAware, MethodCallHandler {
    private val mediaPlayers: MutableMap<String, Player> = HashMap()
    private val handler = Handler()
    private var positionUpdates: Runnable? = null
    internal var seekFinish: Boolean = false

    private var activityAware: ActivityPluginBinding? = null
    private var channel: MethodChannel? = null

    override fun onMethodCall(call: MethodCall, response: MethodChannel.Result) {
        Thread { }.start()
        try {
            handleMethodCall(call, response)
        } catch (e: Exception) {
            LOGGER.log(Level.SEVERE, "Unexpected error!", e)
            response.error("Unexpected error!", e.message, e)
        }
    }

    private fun handleMethodCall(call: MethodCall, response: MethodChannel.Result) {
        val playerId = call.argument<String>("playerId")
        val mode = call.argument<String>("mode")
        val player = getPlayer(playerId, mode)
        when (call.method) {
            "play" -> {
                val url = call.argument<String>("url")
                val volume = call.argument<Double>("volume")!!
                val position = call.argument<Int>("position")
                val respectSilence = call.argument<Boolean>("respectSilence")!!
                val isLocal = call.argument<Boolean>("isLocal")!!
                val stayAwake = call.argument<Boolean>("stayAwake")!!
                player!!.configAttributes(
                    respectSilence,
                    stayAwake,
                    activityAware?.activity?.applicationContext
                )
                player.setVolume(volume)
                Thread {
                    player.setUrl(url, isLocal, activityAware?.activity?.applicationContext)
                    if (position != null && mode != "PlayerMode.LOW_LATENCY") {
                        player.seek(position)
                    }
                    player.play(activityAware?.activity?.applicationContext)
                }.start()
            }
            "resume" -> {
                Thread { player!!.play(activityAware?.activity?.applicationContext) }.start()
            }
            "pause" -> {
                player!!.pause()
            }
            "stop" -> {
                player!!.stop()
            }
            "release" -> {
                player!!.release()
            }
            "seek" -> {
                val position = call.argument<Int>("position")
                Thread { player!!.seek(position!!) }.start()
            }
            "setVolume" -> {
                val volume = call.argument<Double>("volume")!!
                player!!.setVolume(volume)
            }
            "setUrl" -> {
                val url = call.argument<String>("url")
                val isLocal = call.argument<Boolean>("isLocal")!!
                Thread {
                    player!!.setUrl(
                        url,
                        isLocal,
                        activityAware?.activity?.applicationContext
                    )
                }.start()
            }
            "setPlaybackRate" -> {
                val rate = call.argument<Double>("playbackRate")!!
                response.success(player!!.setRate(rate))
                return
            }
            "getDuration" -> {
                response.success(player!!.duration)
                return
            }
            "getCurrentPosition" -> {
                response.success(player!!.currentPosition)
                return
            }
            "setReleaseMode" -> {
                val releaseModeName = call.argument<String>("releaseMode")
                val releaseMode =
                    ReleaseMode.valueOf(releaseModeName!!.substring("ReleaseMode.".length))
                player!!.setReleaseMode(releaseMode)
            }
            "earpieceOrSpeakersToggle" -> {
                val playingRoute = call.argument<String>("playingRoute")
                player!!.setPlayingRoute(playingRoute, activityAware?.activity?.applicationContext)
            }
            else -> {
                response.notImplemented()
                return
            }
        }
        response.success(1)
    }

    private fun getPlayer(playerId: String?, mode: String?): Player? {
        if (!mediaPlayers.containsKey(playerId)) {
            val player =
                if (mode.equals("PlayerMode.MEDIA_PLAYER", ignoreCase = true)) WrappedMediaPlayer(
                    this,
                    playerId!!
                ) else WrappedSoundPool(this, playerId!!)
            mediaPlayers[playerId] = player
        }
        return mediaPlayers[playerId]
    }

    fun handleIsPlaying(player: Player?) {
        startPositionUpdates()
    }

    fun handleDuration(player: Player) {
        channel?.invokeMethod("audio.onDuration", buildArguments(player.playerId, player.duration))
    }

    fun handleCompletion(player: Player) {
        channel?.invokeMethod("audio.onComplete", buildArguments(player.playerId, true))
    }

    fun handleSeekComplete(player: Player?) {
        seekFinish = true
    }

    private fun startPositionUpdates() {
        if (positionUpdates != null) {
            return
        }
        positionUpdates = UpdateCallback(mediaPlayers, channel!!, handler, this)
        handler.post(positionUpdates!!)
    }

    fun stopPositionUpdates() {
        positionUpdates = null
        handler.removeCallbacksAndMessages(null)
    }

    companion object {
        private val LOGGER = Logger.getLogger(AudioplayersPlugin::class.java.canonicalName)
        internal fun buildArguments(playerId: String?, value: Any?): Map<String, Any?> {
            val result: MutableMap<String, Any?> = HashMap()
            result["playerId"] = playerId
            result["value"] = value
            return result
        }
    }

    override fun onAttachedToEngine(flutterPluginBinding: FlutterPluginBinding) {
        channel = MethodChannel(flutterPluginBinding.binaryMessenger, "xyz.luan/audioplayers")
        channel?.setMethodCallHandler(this)
    }

    override fun onDetachedFromEngine(plugin: FlutterPluginBinding) {
        channel?.setMethodCallHandler(null)
    }


    override fun onAttachedToActivity(plugin: ActivityPluginBinding) {
        this.activityAware = plugin
    }

    override fun onDetachedFromActivityForConfigChanges() {
        onDetachedFromActivity()
    }

    override fun onDetachedFromActivity() {
        activityAware = null
    }

    override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
        onAttachedToActivity(binding)
    }


}